@intlayer/core 9.4.4 → 9.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +0 -2
  2. package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +5 -8
  4. package/dist/cjs/interpreter/getPlural.cjs.map +1 -1
  5. package/dist/cjs/localization/getPrefix.cjs +6 -6
  6. package/dist/cjs/localization/getPrefix.cjs.map +1 -1
  7. package/dist/cjs/markdown/compiler.cjs +437 -209
  8. package/dist/cjs/markdown/compiler.cjs.map +1 -1
  9. package/dist/cjs/markdown/constants.cjs +32 -50
  10. package/dist/cjs/markdown/constants.cjs.map +1 -1
  11. package/dist/cjs/markdown/elementScanner.cjs +191 -0
  12. package/dist/cjs/markdown/elementScanner.cjs.map +1 -0
  13. package/dist/cjs/markdown/index.cjs +5 -8
  14. package/dist/cjs/markdown/parser.cjs +90 -48
  15. package/dist/cjs/markdown/parser.cjs.map +1 -1
  16. package/dist/cjs/markdown/renderer.cjs +14 -11
  17. package/dist/cjs/markdown/renderer.cjs.map +1 -1
  18. package/dist/cjs/markdown/utils.cjs +123 -34
  19. package/dist/cjs/markdown/utils.cjs.map +1 -1
  20. package/dist/cjs/utils/getCookie.cjs +1 -1
  21. package/dist/cjs/utils/getCookie.cjs.map +1 -1
  22. package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs.map +1 -1
  23. package/dist/esm/index.mjs +3 -3
  24. package/dist/esm/interpreter/getPlural.mjs.map +1 -1
  25. package/dist/esm/localization/getPrefix.mjs +6 -6
  26. package/dist/esm/localization/getPrefix.mjs.map +1 -1
  27. package/dist/esm/markdown/compiler.mjs +438 -211
  28. package/dist/esm/markdown/compiler.mjs.map +1 -1
  29. package/dist/esm/markdown/constants.mjs +29 -43
  30. package/dist/esm/markdown/constants.mjs.map +1 -1
  31. package/dist/esm/markdown/elementScanner.mjs +183 -0
  32. package/dist/esm/markdown/elementScanner.mjs.map +1 -0
  33. package/dist/esm/markdown/index.mjs +3 -3
  34. package/dist/esm/markdown/parser.mjs +90 -48
  35. package/dist/esm/markdown/parser.mjs.map +1 -1
  36. package/dist/esm/markdown/renderer.mjs +14 -11
  37. package/dist/esm/markdown/renderer.mjs.map +1 -1
  38. package/dist/esm/markdown/utils.mjs +124 -35
  39. package/dist/esm/markdown/utils.mjs.map +1 -1
  40. package/dist/esm/utils/getCookie.mjs +1 -1
  41. package/dist/esm/utils/getCookie.mjs.map +1 -1
  42. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts +3 -3
  43. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts.map +1 -1
  44. package/dist/types/index.d.ts +4 -4
  45. package/dist/types/interpreter/getPlural.d.ts +3 -3
  46. package/dist/types/interpreter/getPlural.d.ts.map +1 -1
  47. package/dist/types/localization/getPrefix.d.ts.map +1 -1
  48. package/dist/types/markdown/compiler.d.ts +43 -0
  49. package/dist/types/markdown/compiler.d.ts.map +1 -1
  50. package/dist/types/markdown/constants.d.ts +26 -17
  51. package/dist/types/markdown/constants.d.ts.map +1 -1
  52. package/dist/types/markdown/elementScanner.d.ts +14 -0
  53. package/dist/types/markdown/elementScanner.d.ts.map +1 -0
  54. package/dist/types/markdown/index.d.ts +4 -4
  55. package/dist/types/markdown/parser.d.ts +0 -5
  56. package/dist/types/markdown/parser.d.ts.map +1 -1
  57. package/dist/types/markdown/renderer.d.ts +0 -4
  58. package/dist/types/markdown/renderer.d.ts.map +1 -1
  59. package/dist/types/markdown/types.d.ts +13 -0
  60. package/dist/types/markdown/types.d.ts.map +1 -1
  61. package/dist/types/markdown/utils.d.ts +7 -2
  62. package/dist/types/markdown/utils.d.ts.map +1 -1
  63. package/package.json +7 -7
  64. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -1
  65. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -1
  66. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -1
  67. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -1
  68. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -1
  69. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -1
  70. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -1
  71. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -7
  72. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -1
  73. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -2
  74. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  75. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -1
  76. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -1
  77. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -1
  78. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -1
  79. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -1
  80. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -1
  81. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -1
  82. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -1
  83. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -7
  84. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -1
  85. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/intlayer/dist/types/index.d.ts +0 -6
  86. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -2
  87. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  88. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -1
  89. package/dist/types/@intlayer/core/dist/types/dictionaryManipulator/plainDynamicLoader.d.ts +0 -2
  90. package/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -2
  91. package/dist/types/@intlayer/core/dist/types/formatters/compact.d.ts +0 -1
  92. package/dist/types/@intlayer/core/dist/types/formatters/currency.d.ts +0 -1
  93. package/dist/types/@intlayer/core/dist/types/formatters/date.d.ts +0 -1
  94. package/dist/types/@intlayer/core/dist/types/formatters/index.d.ts +0 -9
  95. package/dist/types/@intlayer/core/dist/types/formatters/list.d.ts +0 -1
  96. package/dist/types/@intlayer/core/dist/types/formatters/number.d.ts +0 -1
  97. package/dist/types/@intlayer/core/dist/types/formatters/percentage.d.ts +0 -1
  98. package/dist/types/@intlayer/core/dist/types/formatters/relativeTime.d.ts +0 -1
  99. package/dist/types/@intlayer/core/dist/types/formatters/units.d.ts +0 -1
  100. package/dist/types/@intlayer/core/dist/types/interpreter/dictionaryTransformCache.d.ts +0 -4
  101. package/dist/types/@intlayer/core/dist/types/interpreter/getCondition.d.ts +0 -2
  102. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/deepTransform.d.ts +0 -1
  103. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/getContent.d.ts +0 -3
  104. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/index.d.ts +0 -3
  105. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/plugins.d.ts +0 -7
  106. package/dist/types/@intlayer/core/dist/types/interpreter/getDictionary.d.ts +0 -4
  107. package/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -6
  108. package/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -2
  109. package/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -2
  110. package/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -2
  111. package/dist/types/@intlayer/core/dist/types/interpreter/getIntlayer.d.ts +0 -4
  112. package/dist/types/@intlayer/core/dist/types/interpreter/getIntlayerAsync.d.ts +0 -4
  113. package/dist/types/@intlayer/core/dist/types/interpreter/getNesting.d.ts +0 -6
  114. package/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -3
  115. package/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -2
  116. package/dist/types/@intlayer/core/dist/types/interpreter/getTranslation.d.ts +0 -1
  117. package/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -18
  118. package/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -2
  119. package/dist/types/@intlayer/core/dist/types/intlayer/dist/types/index.d.ts +0 -6
  120. package/dist/types/@intlayer/core/dist/types/localization/comparePaths.d.ts +0 -1
  121. package/dist/types/@intlayer/core/dist/types/localization/domainUtils.d.ts +0 -2
  122. package/dist/types/@intlayer/core/dist/types/localization/generateSitemap.d.ts +0 -2
  123. package/dist/types/@intlayer/core/dist/types/localization/getBrowserLocale.d.ts +0 -1
  124. package/dist/types/@intlayer/core/dist/types/localization/getCanonicalPath.d.ts +0 -2
  125. package/dist/types/@intlayer/core/dist/types/localization/getHTMLTextDir.d.ts +0 -1
  126. package/dist/types/@intlayer/core/dist/types/localization/getLocale.d.ts +0 -1
  127. package/dist/types/@intlayer/core/dist/types/localization/getLocaleFromPath.d.ts +0 -2
  128. package/dist/types/@intlayer/core/dist/types/localization/getLocaleLang.d.ts +0 -1
  129. package/dist/types/@intlayer/core/dist/types/localization/getLocaleName.d.ts +0 -1
  130. package/dist/types/@intlayer/core/dist/types/localization/getLocalizedPath.d.ts +0 -2
  131. package/dist/types/@intlayer/core/dist/types/localization/getLocalizedUrl.d.ts +0 -2
  132. package/dist/types/@intlayer/core/dist/types/localization/getMultilingualUrls.d.ts +0 -2
  133. package/dist/types/@intlayer/core/dist/types/localization/getPathWithoutLocale.d.ts +0 -1
  134. package/dist/types/@intlayer/core/dist/types/localization/getPrefix.d.ts +0 -2
  135. package/dist/types/@intlayer/core/dist/types/localization/index.d.ts +0 -22
  136. package/dist/types/@intlayer/core/dist/types/localization/localeDetector.d.ts +0 -2
  137. package/dist/types/@intlayer/core/dist/types/localization/localeMapper.d.ts +0 -1
  138. package/dist/types/@intlayer/core/dist/types/localization/localeResolver.d.ts +0 -1
  139. package/dist/types/@intlayer/core/dist/types/localization/resolveBrowserLocale.d.ts +0 -2
  140. package/dist/types/@intlayer/core/dist/types/localization/rewriteMatch.d.ts +0 -1
  141. package/dist/types/@intlayer/core/dist/types/localization/rewriteUtils.d.ts +0 -2
  142. package/dist/types/@intlayer/core/dist/types/localization/validatePrefix.d.ts +0 -2
  143. package/dist/types/@intlayer/core/dist/types/markdown/index.d.ts +0 -2
  144. package/dist/types/@intlayer/core/dist/types/transpiler/condition/condition.d.ts +0 -1
  145. package/dist/types/@intlayer/core/dist/types/transpiler/condition/index.d.ts +0 -1
  146. package/dist/types/@intlayer/core/dist/types/transpiler/enumeration/enumeration.d.ts +0 -1
  147. package/dist/types/@intlayer/core/dist/types/transpiler/enumeration/index.d.ts +0 -1
  148. package/dist/types/@intlayer/core/dist/types/transpiler/file/file.d.ts +0 -1
  149. package/dist/types/@intlayer/core/dist/types/transpiler/file/index.d.ts +0 -1
  150. package/dist/types/@intlayer/core/dist/types/transpiler/gender/gender.d.ts +0 -1
  151. package/dist/types/@intlayer/core/dist/types/transpiler/gender/index.d.ts +0 -1
  152. package/dist/types/@intlayer/core/dist/types/transpiler/html/html.d.ts +0 -1
  153. package/dist/types/@intlayer/core/dist/types/transpiler/html/index.d.ts +0 -1
  154. package/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -22
  155. package/dist/types/@intlayer/core/dist/types/transpiler/insertion/index.d.ts +0 -1
  156. package/dist/types/@intlayer/core/dist/types/transpiler/insertion/insertion.d.ts +0 -1
  157. package/dist/types/@intlayer/core/dist/types/transpiler/markdown/index.d.ts +0 -1
  158. package/dist/types/@intlayer/core/dist/types/transpiler/markdown/markdown.d.ts +0 -1
  159. package/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  160. package/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -4
  161. package/dist/types/@intlayer/core/dist/types/transpiler/plural/index.d.ts +0 -1
  162. package/dist/types/@intlayer/core/dist/types/transpiler/plural/plural.d.ts +0 -1
  163. package/dist/types/@intlayer/core/dist/types/transpiler/select/index.d.ts +0 -1
  164. package/dist/types/@intlayer/core/dist/types/transpiler/select/select.d.ts +0 -1
  165. package/dist/types/@intlayer/core/dist/types/transpiler/translation/index.d.ts +0 -1
  166. package/dist/types/@intlayer/core/dist/types/transpiler/translation/translation.d.ts +0 -2
  167. package/dist/types/@intlayer/core/dist/types/utils/index.d.ts +0 -3
  168. package/dist/types/@intlayer/core/dist/types/utils/intl.d.ts +0 -1
  169. package/dist/types/@intlayer/core/dist/types/utils/isSameKeyPath.d.ts +0 -1
  170. package/dist/types/@intlayer/core/dist/types/utils/localeStorage.d.ts +0 -2
  171. package/dist/types/intlayer/dist/types/index.d.ts +0 -14
package/README.md CHANGED
@@ -69,8 +69,6 @@ With **per-locale content files**, **TypeScript autocompletion**, **tree-shakabl
69
69
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/interoperability.png?raw=true" alt="Feature" width="700"> | **Interoperability**<br><br>Allow interoperability with react-i18next, next-i18next, next-intl, react-intl, vue-i18n. <br><br> - [Intlayer and react-intl](https://intlayer.org/blog/intlayer-with-react-intl) <br> - [Intlayer and next-intl](https://intlayer.org/blog/intlayer-with-next-intl) <br> - [Intlayer and next-i18next](https://intlayer.org/blog/intlayer-with-next-i18next) <br> - [Intlayer and vue-i18n](https://intlayer.org/blog/intlayer-with-vue-i18n) <br> - [Intlayer compat adapters](https://intlayer.org/doc/compatibility) |
70
70
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/benchmark.png?raw=true" alt="Feature" width="700"> | **Performances & Benchmark**<br><br>Uses advanced tree-shaking and dynamic loading to boost performances and keep the solution as light as possible. <br><br> - [Performances & Benchmark](https://intlayer.org/doc/benchmark) |
71
71
 
72
- ---
73
-
74
72
  ## 📦 Installation
75
73
 
76
74
  Start your journey with Intlayer today and experience a smoother, more powerful approach to internationalization.
@@ -1 +1 @@
1
- {"version":3,"file":"qualifiedDictionary.cjs","names":[],"sources":["../../../src/dictionaryManipulator/qualifiedDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryQualifierType,\n DictionarySelector,\n DictionaryVariantChain,\n DictionaryVariantValue,\n ProviderVariant,\n ProviderVariantMap,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Separator joining per-dimension ids into a composite entry id. Also used as\n * the chunk path separator in dynamic mode.\n */\nexport const COMPOSITE_ID_SEPARATOR = '/';\n\n/**\n * Identity of the implicit fallback variant. A selector that pins no variant\n * resolves to it, and a variant that declares no entry of its own falls back to\n * it — so a key only has to ship the entries that actually differ.\n */\nexport const DEFAULT_VARIANT_ID = 'default';\n\n/**\n * Characters kept verbatim in an encoded qualifier segment. Everything else is\n * percent-encoded so a segment can never contain the composite-id separator\n * (`/`), path-hostile characters (`\\` `:` `*` `?` `\"` `<` `>` `|`, control\n * chars), or characters that would break the generated loader modules (`'`).\n */\nconst SEGMENT_UNSAFE_CHARS = /[^A-Za-z0-9._&=-]/g;\n\n/**\n * Stricter set for the components of an object variant: also encodes `&` and\n * `=` so the `field=value&field=value` serialization stays unambiguous.\n */\nconst COMPONENT_UNSAFE_CHARS = /[^A-Za-z0-9._-]/g;\n\n/** Percent-encodes one UTF-16 code unit as a fixed-width `%XXXX` run. */\nconst percentEncodeChar = (char: string): string =>\n `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`;\n\nconst encodeSegmentText = (raw: string, unsafeChars: RegExp): string => {\n // Bare '%' cannot be produced by encoding (every encoded run is %XXXX),\n // so it is a safe stand-in for the empty string.\n if (raw === '') return '%';\n\n const encoded = raw.replace(unsafeChars, percentEncodeChar);\n\n // '.' and '..' are path navigation on every filesystem — encode the dots.\n if (encoded === '.' || encoded === '..') {\n return encoded.replace(/\\./g, '%002E');\n }\n\n return encoded;\n};\n\n/**\n * Canonical serialization of a single variant value into its identity string —\n * the variant segment of a composite id, the chunk directory name in dynamic\n * mode, and the runtime matching key.\n *\n * - `undefined` → `'default'` (the implicit fallback variant)\n * - a string → the string itself (a named variant)\n * - an object → its sorted `key=value` pairs joined by `&`\n * (e.g. `{ userId: '123', id: 'abc' }` → `'id=abc&userId=123'`)\n *\n * Characters that are unsafe in file paths or generated code are\n * percent-encoded (fixed-width `%XXXX` runs, injective). Common names —\n * letters, digits, `-` `_` `.` — are left untouched. Both the declaration and\n * the selector go through this function, so encoding never affects matching.\n *\n * Two variants resolve to the same entry iff their serializations are equal, so\n * an object variant in a selector must equal the one declared on the dictionary.\n */\nexport const serializeVariant = (\n variant: DictionaryVariantValue | undefined\n): string => {\n if (variant === undefined) return DEFAULT_VARIANT_ID;\n if (typeof variant === 'string') {\n return encodeSegmentText(variant, SEGMENT_UNSAFE_CHARS);\n }\n\n return Object.keys(variant)\n .sort()\n .map(\n (field) =>\n `${encodeSegmentText(field, COMPONENT_UNSAFE_CHARS)}=${encodeSegmentText(String(variant[field]), COMPONENT_UNSAFE_CHARS)}`\n )\n .join('&');\n};\n\n/**\n * Serializes the variant coordinate of a selector into the ordered list of\n * candidate ids to try, so a single value and a preference chain share one code\n * path downstream.\n *\n * - `undefined` → `['default']`\n * - a single value → its one serialization\n * - a chain → one serialization per entry, order preserved\n *\n * An empty chain is treated as \"no variant pinned\" (`['default']`) rather than\n * as an unsatisfiable request.\n */\nexport const serializeVariantChain = (\n variant: DictionaryVariantChain | undefined\n): string[] => {\n if (!Array.isArray(variant)) {\n return [serializeVariant(variant as DictionaryVariantValue | undefined)];\n }\n\n if (variant.length === 0) return [DEFAULT_VARIANT_ID];\n\n return variant.map(serializeVariant);\n};\n\n/**\n * Resolves the variant id a selector actually targets among the ids a key\n * declares — the sparse-override fallback.\n *\n * Candidates are tried in order and the first one the key declares wins.\n * Otherwise the key falls back to its `default` entry, so a variant only has to\n * be declared where its wording differs. When the key declares no `default`\n * either, the first candidate is returned unchanged and the caller resolves to\n * `null` / `[]`.\n *\n * @param requestedVariantIds - The serialized ids the selector asks for, in\n * preference order (a single value is a 1-element\n * list).\n * @param isVariantIdDeclared - Whether the key declares an entry for an id.\n */\nexport const resolveEffectiveVariantId = (\n requestedVariantIds: string[],\n isVariantIdDeclared: (variantId: string) => boolean\n): string => {\n for (const requestedVariantId of requestedVariantIds) {\n if (isVariantIdDeclared(requestedVariantId)) return requestedVariantId;\n }\n\n return isVariantIdDeclared(DEFAULT_VARIANT_ID)\n ? DEFAULT_VARIANT_ID\n : (requestedVariantIds[0] ?? DEFAULT_VARIANT_ID);\n};\n\n/**\n * Tests whether a composite entry id matches a selector across every declared\n * dimension. Segments are compared in their encoded form (both the stored id\n * and the selector go through {@link serializeVariant}). The `item` dimension\n * matches any value when the selector does not provide one (open collection\n * axis); the `variant` dimension is compared against the already-resolved\n * effective id, so the fallback is applied consistently across dimensions.\n */\nconst compositeIdMatchesSelector = (\n compositeId: string,\n qualifierTypes: DictionaryQualifierType[],\n selector: DictionarySelector | undefined,\n effectiveVariantId: string\n): boolean => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n return qualifierTypes.every((qualifierType, index) => {\n if (qualifierType === 'variant') {\n return segments[index] === effectiveVariantId;\n }\n\n // qualifierType === 'item'\n return (\n selector?.item === undefined || segments[index] === String(selector.item)\n );\n });\n};\n\n/**\n * Type guard discriminating a `QualifiedDictionaryGroup` (merge output of a\n * qualified key) from a plain `Dictionary`. Both carry a `content` field; only\n * the group declares `qualifierTypes`, which is therefore the discriminator.\n */\nexport const isQualifiedDictionaryGroup = (\n value: unknown\n): value is QualifiedDictionaryGroup =>\n typeof value === 'object' &&\n value !== null &&\n 'qualifierTypes' in value &&\n Array.isArray((value as { qualifierTypes: unknown }).qualifierTypes) &&\n 'content' in value;\n\n/**\n * Reconstructs a resolvable {@link Dictionary} from a single entry of a\n * qualified group: the content node stored under its composite id, plus the\n * qualifier coordinates decoded from that id (`variant`, `item`).\n *\n * This keeps the resolver's transform code unchanged: it still sees a\n * `{ key, content, variant?, item? }` shape, even though the stored format no\n * longer duplicates those fields per entry. The `variant` coordinate stays in\n * its serialized (encoded) form, e.g. `'id=abc&userId=123'` — matching happens\n * on the composite id segments, never on this reconstructed field.\n */\nexport const reconstructQualifiedEntry = (\n group: QualifiedDictionaryGroup,\n compositeId: string\n): Dictionary => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n const entry = {\n key: group.key,\n content: group.content[compositeId],\n } as Dictionary;\n\n group.qualifierTypes.forEach((qualifierType, index) => {\n if (qualifierType === 'variant') {\n entry.variant = segments[index];\n } else if (qualifierType === 'item') {\n entry.item = Number(segments[index]);\n }\n });\n\n return entry;\n};\n\n/**\n * Resolves a dictionary (or qualified dictionary group) against a selector,\n * across every declared dimension.\n *\n * - Plain dictionary → returned as-is (selector ignored)\n * - `item` declared but not selected → every matching entry ordered by index\n * - `item` selected → the matching entry or null\n * - `variant` defaults to the `default` entry when not selected, and falls back\n * to it when the selected variant declares no entry of its own; an object\n * variant resolves only when the selector provides an equal object (or, again,\n * through the `default` fallback)\n *\n * Dimensions compose: e.g. a variant × item key with `{ variant: 'promo' }`\n * returns every promo item as an array; adding `{ item: 2 }` narrows to one.\n */\nexport const resolveQualifiedDictionary = (\n dictionaryOrGroup: Dictionary | QualifiedDictionaryGroup,\n selector?: DictionarySelector\n): Dictionary | Dictionary[] | null => {\n if (!isQualifiedDictionaryGroup(dictionaryOrGroup)) {\n return dictionaryOrGroup;\n }\n\n const { qualifierTypes, content } = dictionaryOrGroup;\n\n const itemAxisOpen =\n qualifierTypes.includes('item') && selector?.item === undefined;\n\n const compositeIds = Object.keys(content);\n const variantIndex = qualifierTypes.indexOf('variant');\n\n const effectiveVariantId =\n variantIndex === -1\n ? DEFAULT_VARIANT_ID\n : resolveEffectiveVariantId(\n serializeVariantChain(selector?.variant),\n (variantId) =>\n compositeIds.some(\n (compositeId) =>\n compositeId.split(COMPOSITE_ID_SEPARATOR)[variantIndex] ===\n variantId\n )\n );\n\n const matchedEntries = compositeIds\n .filter((compositeId) =>\n compositeIdMatchesSelector(\n compositeId,\n qualifierTypes,\n selector,\n effectiveVariantId\n )\n )\n .map((compositeId) =>\n reconstructQualifiedEntry(dictionaryOrGroup, compositeId)\n );\n\n if (itemAxisOpen) {\n return matchedEntries.sort(\n (left, right) => (left.item ?? 0) - (right.item ?? 0)\n );\n }\n\n return matchedEntries[0] ?? null;\n};\n\n/**\n * Splits the second argument of `getIntlayer` / `getDictionary` into the\n * effective locale and the selector object (if any).\n */\nexport const parseDictionarySelector = <L extends LocalesValues>(\n localeOrSelector?: L | DictionarySelector\n): { locale?: L; selector?: DictionarySelector } => {\n if (typeof localeOrSelector === 'object' && localeOrSelector !== null) {\n return {\n locale: localeOrSelector.locale as L | undefined,\n selector: localeOrSelector,\n };\n }\n\n return { locale: localeOrSelector };\n};\n\n/**\n * Resolves the variant a provider pins for one dictionary key.\n *\n * A string or a chain applies to every key as-is. A plain object is the per-key\n * map: the entry for `dictionaryKey` wins, falling back to the reserved\n * `default` entry, and `undefined` when neither is present (the key then\n * resolves to its own `default` variant, i.e. the behaviour without a provider\n * variant at all).\n *\n * A plain object is **always** the map here — never a structured variant value,\n * which is why a structured variant has to be nested (`{ default: { id } }`).\n *\n * @param providerVariant - The `variant` prop of the surrounding provider.\n * @param dictionaryKey - The key being read.\n */\nexport const resolveProviderVariant = (\n providerVariant: ProviderVariant | undefined,\n dictionaryKey: string\n): DictionaryVariantChain | undefined => {\n if (providerVariant === undefined) return undefined;\n\n if (typeof providerVariant === 'string' || Array.isArray(providerVariant)) {\n return providerVariant as DictionaryVariantChain;\n }\n\n const variantMap = providerVariant as ProviderVariantMap;\n\n return variantMap[dictionaryKey] ?? variantMap[DEFAULT_VARIANT_ID];\n};\n\n/**\n * Builds the effective second argument of a dictionary read by layering the\n * provider defaults under the call-site one — the single place the `locale` and\n * `variant` context defaults are applied, shared by every framework binding.\n *\n * Precedence, per dimension independently:\n * - a call-site selector always wins; `{ variant: 'x' }` **replaces** the\n * provider chain rather than extending it\n * - otherwise the provider value applies\n *\n * Returns a bare locale (not a selector object) whenever no variant is in play,\n * so the existing fast path — and the cache keys built from it — are unchanged\n * for projects that never use variants.\n */\nexport const resolveDictionaryArgument = (params: {\n localeOrSelector?: LocalesValues | DictionarySelector;\n contextLocale?: LocalesValues;\n contextVariant?: ProviderVariant;\n dictionaryKey: string;\n}): LocalesValues | DictionarySelector | undefined => {\n const { localeOrSelector, contextLocale, contextVariant, dictionaryKey } =\n params;\n\n const callSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null\n ? localeOrSelector\n : undefined;\n\n const callLocale = callSelector\n ? callSelector.locale\n : (localeOrSelector as LocalesValues | undefined);\n\n // The context locale is typed as widely as the runtime allows, while a\n // selector narrows `locale` to the declared ones — the value is the same.\n const locale = (callLocale ?? contextLocale) as DictionarySelector['locale'];\n\n // A call-site variant is authoritative; the provider only fills the gap.\n const variant =\n callSelector?.variant ??\n resolveProviderVariant(contextVariant, dictionaryKey);\n\n if (variant === undefined) {\n // Nothing to add: keep the argument in its original shape so the identity\n // built from it stays byte-identical to the pre-variant behaviour.\n return callSelector\n ? { ...callSelector, locale }\n : (locale as LocalesValues | undefined);\n }\n\n return { ...callSelector, locale, variant };\n};\n\n/**\n * Builds a stable string identity of a selector (excluding `locale`), suitable\n * for cache keys and memoization dependencies.\n */\nexport const getDictionarySelectorCacheKey = (\n selector?: DictionarySelector\n): string => {\n if (!selector) return '';\n\n return Object.keys(selector)\n .filter((selectorKey) => selectorKey !== 'locale')\n .sort()\n .map((selectorKey) => {\n const value = selector[selectorKey as keyof DictionarySelector];\n const serialized =\n selectorKey === 'variant'\n ? serializeVariantChain(\n value as DictionaryVariantChain | undefined\n ).join(',')\n : String(value);\n return `${selectorKey}:${serialized}`;\n })\n .join('|');\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"qualifiedDictionary.cjs","names":[],"sources":["../../../src/dictionaryManipulator/qualifiedDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryQualifierType,\n DictionarySelector,\n DictionaryVariantChain,\n DictionaryVariantValue,\n ProviderVariantBase,\n ProviderVariantMap,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Separator joining per-dimension ids into a composite entry id. Also used as\n * the chunk path separator in dynamic mode.\n */\nexport const COMPOSITE_ID_SEPARATOR = '/';\n\n/**\n * Identity of the implicit fallback variant. A selector that pins no variant\n * resolves to it, and a variant that declares no entry of its own falls back to\n * it — so a key only has to ship the entries that actually differ.\n */\nexport const DEFAULT_VARIANT_ID = 'default';\n\n/**\n * Characters kept verbatim in an encoded qualifier segment. Everything else is\n * percent-encoded so a segment can never contain the composite-id separator\n * (`/`), path-hostile characters (`\\` `:` `*` `?` `\"` `<` `>` `|`, control\n * chars), or characters that would break the generated loader modules (`'`).\n */\nconst SEGMENT_UNSAFE_CHARS = /[^A-Za-z0-9._&=-]/g;\n\n/**\n * Stricter set for the components of an object variant: also encodes `&` and\n * `=` so the `field=value&field=value` serialization stays unambiguous.\n */\nconst COMPONENT_UNSAFE_CHARS = /[^A-Za-z0-9._-]/g;\n\n/** Percent-encodes one UTF-16 code unit as a fixed-width `%XXXX` run. */\nconst percentEncodeChar = (char: string): string =>\n `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`;\n\nconst encodeSegmentText = (raw: string, unsafeChars: RegExp): string => {\n // Bare '%' cannot be produced by encoding (every encoded run is %XXXX),\n // so it is a safe stand-in for the empty string.\n if (raw === '') return '%';\n\n const encoded = raw.replace(unsafeChars, percentEncodeChar);\n\n // '.' and '..' are path navigation on every filesystem — encode the dots.\n if (encoded === '.' || encoded === '..') {\n return encoded.replace(/\\./g, '%002E');\n }\n\n return encoded;\n};\n\n/**\n * Canonical serialization of a single variant value into its identity string —\n * the variant segment of a composite id, the chunk directory name in dynamic\n * mode, and the runtime matching key.\n *\n * - `undefined` → `'default'` (the implicit fallback variant)\n * - a string → the string itself (a named variant)\n * - an object → its sorted `key=value` pairs joined by `&`\n * (e.g. `{ userId: '123', id: 'abc' }` → `'id=abc&userId=123'`)\n *\n * Characters that are unsafe in file paths or generated code are\n * percent-encoded (fixed-width `%XXXX` runs, injective). Common names —\n * letters, digits, `-` `_` `.` — are left untouched. Both the declaration and\n * the selector go through this function, so encoding never affects matching.\n *\n * Two variants resolve to the same entry iff their serializations are equal, so\n * an object variant in a selector must equal the one declared on the dictionary.\n */\nexport const serializeVariant = (\n variant: DictionaryVariantValue | undefined\n): string => {\n if (variant === undefined) return DEFAULT_VARIANT_ID;\n if (typeof variant === 'string') {\n return encodeSegmentText(variant, SEGMENT_UNSAFE_CHARS);\n }\n\n return Object.keys(variant)\n .sort()\n .map(\n (field) =>\n `${encodeSegmentText(field, COMPONENT_UNSAFE_CHARS)}=${encodeSegmentText(String(variant[field]), COMPONENT_UNSAFE_CHARS)}`\n )\n .join('&');\n};\n\n/**\n * Serializes the variant coordinate of a selector into the ordered list of\n * candidate ids to try, so a single value and a preference chain share one code\n * path downstream.\n *\n * - `undefined` → `['default']`\n * - a single value → its one serialization\n * - a chain → one serialization per entry, order preserved\n *\n * An empty chain is treated as \"no variant pinned\" (`['default']`) rather than\n * as an unsatisfiable request.\n */\nexport const serializeVariantChain = (\n variant: DictionaryVariantChain | undefined\n): string[] => {\n if (!Array.isArray(variant)) {\n return [serializeVariant(variant as DictionaryVariantValue | undefined)];\n }\n\n if (variant.length === 0) return [DEFAULT_VARIANT_ID];\n\n return variant.map(serializeVariant);\n};\n\n/**\n * Resolves the variant id a selector actually targets among the ids a key\n * declares — the sparse-override fallback.\n *\n * Candidates are tried in order and the first one the key declares wins.\n * Otherwise the key falls back to its `default` entry, so a variant only has to\n * be declared where its wording differs. When the key declares no `default`\n * either, the first candidate is returned unchanged and the caller resolves to\n * `null` / `[]`.\n *\n * @param requestedVariantIds - The serialized ids the selector asks for, in\n * preference order (a single value is a 1-element\n * list).\n * @param isVariantIdDeclared - Whether the key declares an entry for an id.\n */\nexport const resolveEffectiveVariantId = (\n requestedVariantIds: string[],\n isVariantIdDeclared: (variantId: string) => boolean\n): string => {\n for (const requestedVariantId of requestedVariantIds) {\n if (isVariantIdDeclared(requestedVariantId)) return requestedVariantId;\n }\n\n return isVariantIdDeclared(DEFAULT_VARIANT_ID)\n ? DEFAULT_VARIANT_ID\n : (requestedVariantIds[0] ?? DEFAULT_VARIANT_ID);\n};\n\n/**\n * Tests whether a composite entry id matches a selector across every declared\n * dimension. Segments are compared in their encoded form (both the stored id\n * and the selector go through {@link serializeVariant}). The `item` dimension\n * matches any value when the selector does not provide one (open collection\n * axis); the `variant` dimension is compared against the already-resolved\n * effective id, so the fallback is applied consistently across dimensions.\n */\nconst compositeIdMatchesSelector = (\n compositeId: string,\n qualifierTypes: DictionaryQualifierType[],\n selector: DictionarySelector | undefined,\n effectiveVariantId: string\n): boolean => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n return qualifierTypes.every((qualifierType, index) => {\n if (qualifierType === 'variant') {\n return segments[index] === effectiveVariantId;\n }\n\n // qualifierType === 'item'\n return (\n selector?.item === undefined || segments[index] === String(selector.item)\n );\n });\n};\n\n/**\n * Type guard discriminating a `QualifiedDictionaryGroup` (merge output of a\n * qualified key) from a plain `Dictionary`. Both carry a `content` field; only\n * the group declares `qualifierTypes`, which is therefore the discriminator.\n */\nexport const isQualifiedDictionaryGroup = (\n value: unknown\n): value is QualifiedDictionaryGroup =>\n typeof value === 'object' &&\n value !== null &&\n 'qualifierTypes' in value &&\n Array.isArray((value as { qualifierTypes: unknown }).qualifierTypes) &&\n 'content' in value;\n\n/**\n * Reconstructs a resolvable {@link Dictionary} from a single entry of a\n * qualified group: the content node stored under its composite id, plus the\n * qualifier coordinates decoded from that id (`variant`, `item`).\n *\n * This keeps the resolver's transform code unchanged: it still sees a\n * `{ key, content, variant?, item? }` shape, even though the stored format no\n * longer duplicates those fields per entry. The `variant` coordinate stays in\n * its serialized (encoded) form, e.g. `'id=abc&userId=123'` — matching happens\n * on the composite id segments, never on this reconstructed field.\n */\nexport const reconstructQualifiedEntry = (\n group: QualifiedDictionaryGroup,\n compositeId: string\n): Dictionary => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n const entry = {\n key: group.key,\n content: group.content[compositeId],\n } as Dictionary;\n\n group.qualifierTypes.forEach((qualifierType, index) => {\n if (qualifierType === 'variant') {\n entry.variant = segments[index];\n } else if (qualifierType === 'item') {\n entry.item = Number(segments[index]);\n }\n });\n\n return entry;\n};\n\n/**\n * Resolves a dictionary (or qualified dictionary group) against a selector,\n * across every declared dimension.\n *\n * - Plain dictionary → returned as-is (selector ignored)\n * - `item` declared but not selected → every matching entry ordered by index\n * - `item` selected → the matching entry or null\n * - `variant` defaults to the `default` entry when not selected, and falls back\n * to it when the selected variant declares no entry of its own; an object\n * variant resolves only when the selector provides an equal object (or, again,\n * through the `default` fallback)\n *\n * Dimensions compose: e.g. a variant × item key with `{ variant: 'promo' }`\n * returns every promo item as an array; adding `{ item: 2 }` narrows to one.\n */\nexport const resolveQualifiedDictionary = (\n dictionaryOrGroup: Dictionary | QualifiedDictionaryGroup,\n selector?: DictionarySelector\n): Dictionary | Dictionary[] | null => {\n if (!isQualifiedDictionaryGroup(dictionaryOrGroup)) {\n return dictionaryOrGroup;\n }\n\n const { qualifierTypes, content } = dictionaryOrGroup;\n\n const itemAxisOpen =\n qualifierTypes.includes('item') && selector?.item === undefined;\n\n const compositeIds = Object.keys(content);\n const variantIndex = qualifierTypes.indexOf('variant');\n\n const effectiveVariantId =\n variantIndex === -1\n ? DEFAULT_VARIANT_ID\n : resolveEffectiveVariantId(\n serializeVariantChain(selector?.variant),\n (variantId) =>\n compositeIds.some(\n (compositeId) =>\n compositeId.split(COMPOSITE_ID_SEPARATOR)[variantIndex] ===\n variantId\n )\n );\n\n const matchedEntries = compositeIds\n .filter((compositeId) =>\n compositeIdMatchesSelector(\n compositeId,\n qualifierTypes,\n selector,\n effectiveVariantId\n )\n )\n .map((compositeId) =>\n reconstructQualifiedEntry(dictionaryOrGroup, compositeId)\n );\n\n if (itemAxisOpen) {\n return matchedEntries.sort(\n (left, right) => (left.item ?? 0) - (right.item ?? 0)\n );\n }\n\n return matchedEntries[0] ?? null;\n};\n\n/**\n * Splits the second argument of `getIntlayer` / `getDictionary` into the\n * effective locale and the selector object (if any).\n */\nexport const parseDictionarySelector = <L extends LocalesValues>(\n localeOrSelector?: L | DictionarySelector\n): { locale?: L; selector?: DictionarySelector } => {\n if (typeof localeOrSelector === 'object' && localeOrSelector !== null) {\n return {\n locale: localeOrSelector.locale as L | undefined,\n selector: localeOrSelector,\n };\n }\n\n return { locale: localeOrSelector };\n};\n\n/**\n * Resolves the variant a provider pins for one dictionary key.\n *\n * A string or a chain applies to every key as-is. A plain object is the per-key\n * map: the entry for `dictionaryKey` wins, falling back to the reserved\n * `default` entry, and `undefined` when neither is present (the key then\n * resolves to its own `default` variant, i.e. the behaviour without a provider\n * variant at all).\n *\n * A plain object is **always** the map here — never a structured variant value,\n * which is why a structured variant has to be nested (`{ default: { id } }`).\n *\n * @param providerVariant - The `variant` prop of the surrounding provider.\n * @param dictionaryKey - The key being read.\n */\nexport const resolveProviderVariant = (\n providerVariant: ProviderVariantBase | undefined,\n dictionaryKey: string\n): DictionaryVariantChain | undefined => {\n if (providerVariant === undefined) return undefined;\n\n if (typeof providerVariant === 'string' || Array.isArray(providerVariant)) {\n return providerVariant as DictionaryVariantChain;\n }\n\n const variantMap = providerVariant as ProviderVariantMap;\n\n return variantMap[dictionaryKey] ?? variantMap[DEFAULT_VARIANT_ID];\n};\n\n/**\n * Builds the effective second argument of a dictionary read by layering the\n * provider defaults under the call-site one — the single place the `locale` and\n * `variant` context defaults are applied, shared by every framework binding.\n *\n * Precedence, per dimension independently:\n * - a call-site selector always wins; `{ variant: 'x' }` **replaces** the\n * provider chain rather than extending it\n * - otherwise the provider value applies\n *\n * Returns a bare locale (not a selector object) whenever no variant is in play,\n * so the existing fast path — and the cache keys built from it — are unchanged\n * for projects that never use variants.\n */\nexport const resolveDictionaryArgument = (params: {\n localeOrSelector?: LocalesValues | DictionarySelector;\n contextLocale?: LocalesValues;\n contextVariant?: ProviderVariantBase;\n dictionaryKey: string;\n}): LocalesValues | DictionarySelector | undefined => {\n const { localeOrSelector, contextLocale, contextVariant, dictionaryKey } =\n params;\n\n const callSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null\n ? localeOrSelector\n : undefined;\n\n const callLocale = callSelector\n ? callSelector.locale\n : (localeOrSelector as LocalesValues | undefined);\n\n // The context locale is typed as widely as the runtime allows, while a\n // selector narrows `locale` to the declared ones — the value is the same.\n const locale = (callLocale ?? contextLocale) as DictionarySelector['locale'];\n\n // A call-site variant is authoritative; the provider only fills the gap.\n const variant =\n callSelector?.variant ??\n resolveProviderVariant(contextVariant, dictionaryKey);\n\n if (variant === undefined) {\n // Nothing to add: keep the argument in its original shape so the identity\n // built from it stays byte-identical to the pre-variant behaviour.\n return callSelector\n ? { ...callSelector, locale }\n : (locale as LocalesValues | undefined);\n }\n\n return { ...callSelector, locale, variant };\n};\n\n/**\n * Builds a stable string identity of a selector (excluding `locale`), suitable\n * for cache keys and memoization dependencies.\n */\nexport const getDictionarySelectorCacheKey = (\n selector?: DictionarySelector\n): string => {\n if (!selector) return '';\n\n return Object.keys(selector)\n .filter((selectorKey) => selectorKey !== 'locale')\n .sort()\n .map((selectorKey) => {\n const value = selector[selectorKey as keyof DictionarySelector];\n const serialized =\n selectorKey === 'variant'\n ? serializeVariantChain(\n value as DictionaryVariantChain | undefined\n ).join(',')\n : String(value);\n return `${selectorKey}:${serialized}`;\n })\n .join('|');\n};\n"],"mappings":"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"}
@@ -131,8 +131,6 @@ exports.CODE_INLINE_R = require_markdown_constants.CODE_INLINE_R;
131
131
  exports.COMPOSITE_ID_SEPARATOR = require_dictionaryManipulator_qualifiedDictionary.COMPOSITE_ID_SEPARATOR;
132
132
  exports.CONSECUTIVE_NEWLINE_R = require_markdown_constants.CONSECUTIVE_NEWLINE_R;
133
133
  exports.CR_NEWLINE_R = require_markdown_constants.CR_NEWLINE_R;
134
- exports.CUSTOM_COMPONENT_OPENING_TAG_R = require_markdown_constants.CUSTOM_COMPONENT_OPENING_TAG_R;
135
- exports.CUSTOM_COMPONENT_R = require_markdown_constants.CUSTOM_COMPONENT_R;
136
134
  exports.CachedIntl = require_utils_intl.CachedIntl;
137
135
  exports.Intl = require_utils_intl.CachedIntl;
138
136
  exports.DEFAULT_VARIANT_ID = require_dictionaryManipulator_qualifiedDictionary.DEFAULT_VARIANT_ID;
@@ -146,14 +144,10 @@ exports.GFM_TASK_R = require_markdown_constants.GFM_TASK_R;
146
144
  exports.HEADING_ATX_COMPLIANT_R = require_markdown_constants.HEADING_ATX_COMPLIANT_R;
147
145
  exports.HEADING_R = require_markdown_constants.HEADING_R;
148
146
  exports.HEADING_SETEXT_R = require_markdown_constants.HEADING_SETEXT_R;
149
- exports.HTML_BLOCK_ELEMENT_R = require_markdown_constants.HTML_BLOCK_ELEMENT_R;
150
- exports.HTML_BLOCK_OPENING_TAG_R = require_markdown_constants.HTML_BLOCK_OPENING_TAG_R;
151
147
  exports.HTML_CHAR_CODE_R = require_markdown_constants.HTML_CHAR_CODE_R;
152
148
  exports.HTML_COMMENT_R = require_markdown_constants.HTML_COMMENT_R;
153
149
  exports.HTML_CUSTOM_ATTR_R = require_markdown_constants.HTML_CUSTOM_ATTR_R;
154
150
  exports.HTML_LEFT_TRIM_AMOUNT_R = require_markdown_constants.HTML_LEFT_TRIM_AMOUNT_R;
155
- exports.HTML_SELF_CLOSING_ELEMENT_R = require_markdown_constants.HTML_SELF_CLOSING_ELEMENT_R;
156
- exports.HTML_SELF_CLOSING_OPENING_TAG_R = require_markdown_constants.HTML_SELF_CLOSING_OPENING_TAG_R;
157
151
  exports.HTML_TAGS = require_transpiler_html_htmlTags.HTML_TAGS;
158
152
  exports.INLINE_SKIP_R = require_markdown_constants.INLINE_SKIP_R;
159
153
  exports.INTERPOLATION_R = require_markdown_constants.INTERPOLATION_R;
@@ -164,7 +158,6 @@ exports.LocaleStorage = require_utils_localeStorage.LocaleStorage;
164
158
  exports.LocaleStorageClient = require_utils_localeStorage.LocaleStorageClient;
165
159
  exports.LocaleStorageServer = require_utils_localeStorage.LocaleStorageServer;
166
160
  exports.NAMED_CODES_TO_UNICODE = require_markdown_constants.NAMED_CODES_TO_UNICODE;
167
- exports.NORMALIZE_WHITESPACE_R = require_markdown_constants.NORMALIZE_WHITESPACE_R;
168
161
  exports.NP_TABLE_R = require_markdown_constants.NP_TABLE_R;
169
162
  exports.ORDERED = require_markdown_constants.ORDERED;
170
163
  exports.ORDERED_LIST_BULLET = require_markdown_constants.ORDERED_LIST_BULLET;
@@ -180,7 +173,11 @@ exports.QUALIFIER_ORDER = require_dictionaryManipulator_qualifiedDictionaryDecla
180
173
  exports.REFERENCE_IMAGE_OR_LINK = require_markdown_constants.REFERENCE_IMAGE_OR_LINK;
181
174
  exports.REFERENCE_IMAGE_R = require_markdown_constants.REFERENCE_IMAGE_R;
182
175
  exports.REFERENCE_LINK_R = require_markdown_constants.REFERENCE_LINK_R;
176
+ exports.RuleScope = require_markdown_constants.RuleScope;
183
177
  exports.RuleType = require_markdown_constants.RuleType;
178
+ exports.SCOPE_BLOCK = require_markdown_constants.SCOPE_BLOCK;
179
+ exports.SCOPE_BOTH = require_markdown_constants.SCOPE_BOTH;
180
+ exports.SCOPE_INLINE = require_markdown_constants.SCOPE_INLINE;
184
181
  exports.SHORTCODE_R = require_markdown_constants.SHORTCODE_R;
185
182
  exports.SHOULD_RENDER_AS_BLOCK_R = require_markdown_constants.SHOULD_RENDER_AS_BLOCK_R;
186
183
  exports.TABLE_CENTER_ALIGN = require_markdown_constants.TABLE_CENTER_ALIGN;
@@ -192,7 +189,6 @@ exports.TEXT_BOLD_R = require_markdown_constants.TEXT_BOLD_R;
192
189
  exports.TEXT_EMPHASIZED_R = require_markdown_constants.TEXT_EMPHASIZED_R;
193
190
  exports.TEXT_ESCAPED_R = require_markdown_constants.TEXT_ESCAPED_R;
194
191
  exports.TEXT_MARKED_R = require_markdown_constants.TEXT_MARKED_R;
195
- exports.TEXT_PLAIN_R = require_markdown_constants.TEXT_PLAIN_R;
196
192
  exports.TEXT_STRIKETHROUGHED_R = require_markdown_constants.TEXT_STRIKETHROUGHED_R;
197
193
  exports.TRIM_STARTING_NEWLINES = require_markdown_constants.TRIM_STARTING_NEWLINES;
198
194
  exports.UNESCAPE_R = require_markdown_constants.UNESCAPE_R;
@@ -313,6 +309,7 @@ exports.getPreloadedDictionary = require_dictionaryManipulator_preloadedDynamicL
313
309
  exports.getReplacedValuesContent = require_deepTransformPlugins_getReplacedValuesContent.getReplacedValuesContent;
314
310
  exports.getRewritePath = require_localization_rewriteUtils.getRewritePath;
315
311
  exports.getRewriteRules = require_localization_rewriteMatch.getRewriteRules;
312
+ exports.getRuleSet = require_markdown_compiler.getRuleSet;
316
313
  exports.getSelect = require_interpreter_getSelect.getSelect;
317
314
  exports.getSplittedContent = require_deepTransformPlugins_getSplittedContent.getSplittedContent;
318
315
  exports.getSplittedDictionaryContent = require_deepTransformPlugins_getSplittedContent.getSplittedDictionaryContent;
@@ -1 +1 @@
1
- {"version":3,"file":"getPlural.cjs","names":["getCachedIntl"],"sources":["../../../src/interpreter/getPlural.ts"],"sourcesContent":["import type { DeclaredLocales } from '@intlayer/types';\nimport type { LocalesValues } from 'intlayer';\nimport { getCachedIntl } from '../formatters';\nimport type {\n PluralCategory,\n PluralContentState,\n} from '../transpiler/plural/plural';\n\n/**\n * Picks content from a plural map based on a count and locale, using CLDR\n * pluralization rules (`Intl.PluralRules`).\n *\n * Falls back to the `other` category when no specific category matches.\n *\n * @example\n * ```ts\n * getPlural({\n * one: 'one item',\n * other: '{{count}} items',\n * }, 5, 'en');\n * // '{{count}} items'\n * ```\n */\nexport const getPlural = <const L extends LocalesValues = DeclaredLocales>(\n pluralContent: PluralContentState<string>,\n count: number,\n locale: L\n): string => {\n const category = getCachedIntl('PluralRules', locale).select(\n count\n ) as PluralCategory;\n\n return pluralContent[category] ?? pluralContent.other;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuBA,MAAa,aACX,eACA,OACA,WACW;CAKX,OAAO,cAJUA,iCAAc,eAAe,MAAM,CAAC,CAAC,OACpD,KAG0B,MAAM,cAAc;AAClD"}
1
+ {"version":3,"file":"getPlural.cjs","names":["getCachedIntl"],"sources":["../../../src/interpreter/getPlural.ts"],"sourcesContent":["import type { DeclaredLocales } from '@intlayer/types';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { getCachedIntl } from '../formatters';\nimport type {\n PluralCategory,\n PluralContentState,\n} from '../transpiler/plural/plural';\n\n/**\n * Picks content from a plural map based on a count and locale, using CLDR\n * pluralization rules (`Intl.PluralRules`).\n *\n * Falls back to the `other` category when no specific category matches.\n *\n * @example\n * ```ts\n * getPlural({\n * one: 'one item',\n * other: '{{count}} items',\n * }, 5, 'en');\n * // '{{count}} items'\n * ```\n */\nexport const getPlural = <const L extends LocalesValues = DeclaredLocales>(\n pluralContent: PluralContentState<string>,\n count: number,\n locale: L\n): string => {\n const category = getCachedIntl('PluralRules', locale).select(\n count\n ) as PluralCategory;\n\n return pluralContent[category] ?? pluralContent.other;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuBA,MAAa,aACX,eACA,OACA,WACW;CAKX,OAAO,cAJUA,iCAAc,eAAe,MAAM,CAAC,CAAC,OACpD,KAG0B,MAAM,cAAc;AAClD"}
@@ -9,12 +9,12 @@ let _intlayer_config_defaultValues = require("@intlayer/config/defaultValues");
9
9
  * Single source of truth for default routing config resolution across all localization functions.
10
10
  */
11
11
  const resolveRoutingConfig = (options = {}) => ({
12
- defaultLocale: _intlayer_config_built.internationalization?.defaultLocale ?? _intlayer_config_defaultValues.DEFAULT_LOCALE,
13
- mode: _intlayer_config_built.routing?.mode ?? _intlayer_config_defaultValues.ROUTING_MODE,
14
- locales: _intlayer_config_built.internationalization?.locales ?? _intlayer_config_defaultValues.LOCALES,
15
- rewrite: _intlayer_config_built.routing?.rewrite,
16
- domains: _intlayer_config_built.routing?.domains,
17
- ...options
12
+ ...options,
13
+ defaultLocale: options.defaultLocale ?? _intlayer_config_built.internationalization?.defaultLocale ?? _intlayer_config_defaultValues.DEFAULT_LOCALE,
14
+ mode: options.mode ?? _intlayer_config_built.routing?.mode ?? _intlayer_config_defaultValues.ROUTING_MODE,
15
+ locales: options.locales ?? _intlayer_config_built.internationalization?.locales ?? _intlayer_config_defaultValues.LOCALES,
16
+ rewrite: options.rewrite ?? _intlayer_config_built.routing?.rewrite,
17
+ domains: options.domains ?? _intlayer_config_built.routing?.domains
18
18
  });
19
19
  /**
20
20
  * Narrows an arbitrary string to a declared locale by membership test.
@@ -1 +1 @@
1
- {"version":3,"file":"getPrefix.cjs","names":["internationalization","DEFAULT_LOCALE","routing","ROUTING_MODE","LOCALES","isLocaleExclusiveOnDomain"],"sources":["../../../src/localization/getPrefix.ts"],"sourcesContent":["import { internationalization, routing } from '@intlayer/config/built';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n ROUTING_MODE,\n} from '@intlayer/config/defaultValues';\nimport type { RewriteRules, RoutingConfig } from '@intlayer/types/config';\nimport type {\n DeclaredLocales,\n DomainRuleMap,\n IsDomainExclusive,\n LocalesValues,\n ResolvedDefaultLocale,\n ResolvedDomains,\n ResolvedRoutingMode,\n} from '@intlayer/types/module_augmentation';\nimport { isLocaleExclusiveOnDomain } from './domainUtils';\n\n/**\n * Shared routing options used across all URL localization functions.\n */\nexport type RoutingOptions = {\n locales?: LocalesValues[];\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n rewrite?: RoutingConfig['rewrite'] | RewriteRules;\n domains?: RoutingConfig['domains'];\n /**\n * The hostname of the page currently being rendered (e.g. `'intlayer.org'`).\n * When provided, `getLocalizedUrl` returns a relative URL for locales whose\n * configured domain matches `currentDomain`, and an absolute URL only when\n * the target locale lives on a different domain.\n *\n * When omitted the function tries to infer it from:\n * 1. The domain of an absolute input URL.\n * 2. `window.location.hostname` in browser environments.\n * Falls back to always generating absolute URLs when neither is available.\n */\n currentDomain?: string;\n};\n\n/**\n * Resolves routing configuration by merging provided options with configuration defaults.\n * Single source of truth for default routing config resolution across all localization functions.\n */\nexport const resolveRoutingConfig = (\n options: RoutingOptions = {}\n): Omit<RoutingOptions, 'defaultLocale' | 'mode' | 'locales'> & {\n defaultLocale: DeclaredLocales;\n mode: RoutingConfig['mode'];\n locales: DeclaredLocales[];\n} => ({\n defaultLocale: internationalization?.defaultLocale ?? DEFAULT_LOCALE,\n mode: routing?.mode ?? ROUTING_MODE,\n locales: internationalization?.locales ?? LOCALES,\n rewrite: routing?.rewrite,\n domains: routing?.domains,\n ...options,\n});\n\n/**\n * Narrows an arbitrary string to a declared locale by membership test.\n *\n * `locales` is typed as `DeclaredLocales[]`, so a plain `includes` rejects the\n * `string` values that actually need testing (a URL segment, a query param, a\n * stored value). This keeps the check honest and hands back a narrowed type\n * instead of forcing a cast at every call site.\n *\n * @param value - The candidate locale, from a URL segment / storage / header.\n * @param locales - The locales declared by the configuration.\n * @returns Whether `value` is one of the declared locales.\n */\nexport const isDeclaredLocale = (\n value: LocalesValues | undefined | null,\n locales?: readonly LocalesValues[]\n): value is DeclaredLocales =>\n !!value && (locales ?? internationalization.locales).includes(value);\n\nexport type GetPrefixOptions = {\n defaultLocale?: ResolvedDefaultLocale;\n mode?: RoutingConfig['mode'];\n};\n\nexport type GetPrefixResult = {\n /**\n * The locale path segment appended to `/`, with a trailing slash (e.g. `'fr/'`).\n * Empty string when no prefix is needed.\n */\n prefix: string;\n /**\n * The bare locale identifier (e.g. `'fr'`), or `undefined` when no prefix is applied.\n */\n localePrefix: DeclaredLocales | undefined;\n};\n\n/**\n * Narrowed return type for {@link getPrefix} that carries the locale literal through.\n *\n * Distributes over union locales — calling `getPrefix('fr')` in `prefix-no-default`\n * mode with `defaultLocale = 'en'` resolves to `{ prefix: 'fr/'; localePrefix: 'fr' }`.\n * A locale served from its own exclusive domain resolves to an empty prefix, as\n * the hostname already identifies it.\n *\n * Note: the \"locale not in locales\" edge case returns an empty result at runtime\n * regardless of what this type reports.\n */\nexport type GetPrefixResultNarrowed<\n L extends LocalesValues | undefined,\n Mode extends string = ResolvedRoutingMode,\n Default extends LocalesValues = ResolvedDefaultLocale,\n Domains extends DomainRuleMap = ResolvedDomains,\n> = L extends string\n ? [string] extends [L] // L is wide (string / LocalesValues) → distribute over declared locales\n ? GetPrefixResultNarrowed<DeclaredLocales, Mode, Default, Domains>\n : IsDomainExclusive<L, Domains> extends true\n ? { prefix: ''; localePrefix: undefined } // the domain identifies the locale\n : [string] extends [Mode]\n ? GetPrefixResult // mode is wide → fall back to generic result\n : Mode extends 'prefix-all'\n ? { prefix: `${L}/`; localePrefix: L }\n : Mode extends 'prefix-no-default'\n ? L extends Default\n ? { prefix: ''; localePrefix: undefined }\n : { prefix: `${L}/`; localePrefix: L }\n : { prefix: ''; localePrefix: undefined } // no-prefix / search-params\n : { prefix: ''; localePrefix: undefined }; // locale is undefined\n\n/**\n * Determines the URL prefix for a given locale based on the routing mode configuration.\n *\n * Example:\n *\n * ```ts\n * // prefix-no-default mode with default locale\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // prefix-no-default mode with non-default locale\n * getPrefix('fr', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '/fr', localePrefix: 'fr' }\n *\n * // prefix-all mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-all' })\n * // Returns { prefix: '/en', localePrefix: locale }\n *\n * // search-params mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'search-params' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // no-prefix mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'no-prefix' })\n * // Returns { prefix: '', localePrefix: undefined }\n * ```\n *\n * @param locale - The locale to check for prefix. If not provided, uses configured default locale.\n * @param options - Configuration options\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @returns An object containing pathPrefix, prefix, and localePrefix for the given locale.\n */\nexport const getPrefix = <const L extends LocalesValues | undefined>(\n locale: L,\n options: RoutingOptions = {}\n): GetPrefixResultNarrowed<L> => {\n const { defaultLocale, mode, locales, domains } =\n resolveRoutingConfig(options);\n\n if (\n (process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'prefix-all' &&\n process.env.INTLAYER_ROUTING_MODE !== 'prefix-no-default') ||\n !locale ||\n !isDeclaredLocale(locale, locales)\n ) {\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n }\n\n // If this locale is the only one assigned to its domain, no URL prefix is needed\n // (the domain itself identifies the locale). Shared domains use normal prefix logic.\n if (\n process.env.INTLAYER_ROUTING_DOMAINS !== 'false' &&\n isLocaleExclusiveOnDomain(locale as LocalesValues, domains)\n ) {\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n }\n\n // Handle prefix-based modes (prefix-all or prefix-no-default)\n const shouldPrefix =\n mode === 'prefix-all' ||\n (mode === 'prefix-no-default' && defaultLocale !== locale);\n\n if (shouldPrefix) {\n return {\n prefix: `${locale}/`,\n localePrefix: locale as DeclaredLocales,\n } as GetPrefixResultNarrowed<L>;\n }\n\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n};\n"],"mappings":";;;;;;;;;;AA6CA,MAAa,wBACX,UAA0B,CAAC,OAKvB;CACJ,eAAeA,6CAAsB,iBAAiBC;CACtD,MAAMC,gCAAS,QAAQC;CACvB,SAASH,6CAAsB,WAAWI;CAC1C,SAASF,gCAAS;CAClB,SAASA,gCAAS;CAClB,GAAG;AACL;;;;;;;;;;;;;AAcA,MAAa,oBACX,OACA,YAEA,CAAC,CAAC,UAAU,WAAWF,4CAAqB,QAAO,CAAE,SAAS,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFrE,MAAa,aACX,QACA,UAA0B,CAAC,MACI;CAC/B,MAAM,EAAE,eAAe,MAAM,SAAS,YACpC,qBAAqB,OAAO;CAE9B,IACG,QAAQ,IAAI,yBACX,QAAQ,IAAI,0BAA0B,gBACtC,QAAQ,IAAI,0BAA0B,uBACxC,CAAC,UACD,CAAC,iBAAiB,QAAQ,OAAO,GAEjC,OAAO;EACL,QAAQ;EACR,cAAc;CAChB;CAKF,IACE,QAAQ,IAAI,6BAA6B,WACzCK,2DAA0B,QAAyB,OAAO,GAE1D,OAAO;EACL,QAAQ;EACR,cAAc;CAChB;CAQF,IAHE,SAAS,gBACR,SAAS,uBAAuB,kBAAkB,QAGnD,OAAO;EACL,QAAQ,GAAG,OAAO;EAClB,cAAc;CAChB;CAGF,OAAO;EACL,QAAQ;EACR,cAAc;CAChB;AACF"}
1
+ {"version":3,"file":"getPrefix.cjs","names":["internationalization","DEFAULT_LOCALE","routing","ROUTING_MODE","LOCALES","isLocaleExclusiveOnDomain"],"sources":["../../../src/localization/getPrefix.ts"],"sourcesContent":["import { internationalization, routing } from '@intlayer/config/built';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n ROUTING_MODE,\n} from '@intlayer/config/defaultValues';\nimport type { RewriteRules, RoutingConfig } from '@intlayer/types/config';\nimport type {\n DeclaredLocales,\n DomainRuleMap,\n IsDomainExclusive,\n LocalesValues,\n ResolvedDefaultLocale,\n ResolvedDomains,\n ResolvedRoutingMode,\n} from '@intlayer/types/module_augmentation';\nimport { isLocaleExclusiveOnDomain } from './domainUtils';\n\n/**\n * Shared routing options used across all URL localization functions.\n */\nexport type RoutingOptions = {\n locales?: LocalesValues[];\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n rewrite?: RoutingConfig['rewrite'] | RewriteRules;\n domains?: RoutingConfig['domains'];\n /**\n * The hostname of the page currently being rendered (e.g. `'intlayer.org'`).\n * When provided, `getLocalizedUrl` returns a relative URL for locales whose\n * configured domain matches `currentDomain`, and an absolute URL only when\n * the target locale lives on a different domain.\n *\n * When omitted the function tries to infer it from:\n * 1. The domain of an absolute input URL.\n * 2. `window.location.hostname` in browser environments.\n * Falls back to always generating absolute URLs when neither is available.\n */\n currentDomain?: string;\n};\n\n/**\n * Resolves routing configuration by merging provided options with configuration defaults.\n * Single source of truth for default routing config resolution across all localization functions.\n */\nexport const resolveRoutingConfig = (\n options: RoutingOptions = {}\n): Omit<RoutingOptions, 'defaultLocale' | 'mode' | 'locales'> & {\n defaultLocale: DeclaredLocales;\n mode: RoutingConfig['mode'];\n locales: DeclaredLocales[];\n} => ({\n ...options,\n defaultLocale: (options.defaultLocale ??\n internationalization?.defaultLocale ??\n DEFAULT_LOCALE) as DeclaredLocales,\n mode: options.mode ?? routing?.mode ?? ROUTING_MODE,\n locales: (options.locales ??\n internationalization?.locales ??\n LOCALES) as DeclaredLocales[],\n rewrite: options.rewrite ?? routing?.rewrite,\n domains: options.domains ?? routing?.domains,\n});\n\n/**\n * Narrows an arbitrary string to a declared locale by membership test.\n *\n * `locales` is typed as `DeclaredLocales[]`, so a plain `includes` rejects the\n * `string` values that actually need testing (a URL segment, a query param, a\n * stored value). This keeps the check honest and hands back a narrowed type\n * instead of forcing a cast at every call site.\n *\n * @param value - The candidate locale, from a URL segment / storage / header.\n * @param locales - The locales declared by the configuration.\n * @returns Whether `value` is one of the declared locales.\n */\nexport const isDeclaredLocale = (\n value: LocalesValues | undefined | null,\n locales?: readonly LocalesValues[]\n): value is DeclaredLocales =>\n !!value && (locales ?? internationalization.locales).includes(value);\n\nexport type GetPrefixOptions = {\n defaultLocale?: ResolvedDefaultLocale;\n mode?: RoutingConfig['mode'];\n};\n\nexport type GetPrefixResult = {\n /**\n * The locale path segment appended to `/`, with a trailing slash (e.g. `'fr/'`).\n * Empty string when no prefix is needed.\n */\n prefix: string;\n /**\n * The bare locale identifier (e.g. `'fr'`), or `undefined` when no prefix is applied.\n */\n localePrefix: DeclaredLocales | undefined;\n};\n\n/**\n * Narrowed return type for {@link getPrefix} that carries the locale literal through.\n *\n * Distributes over union locales — calling `getPrefix('fr')` in `prefix-no-default`\n * mode with `defaultLocale = 'en'` resolves to `{ prefix: 'fr/'; localePrefix: 'fr' }`.\n * A locale served from its own exclusive domain resolves to an empty prefix, as\n * the hostname already identifies it.\n *\n * Note: the \"locale not in locales\" edge case returns an empty result at runtime\n * regardless of what this type reports.\n */\nexport type GetPrefixResultNarrowed<\n L extends LocalesValues | undefined,\n Mode extends string = ResolvedRoutingMode,\n Default extends LocalesValues = ResolvedDefaultLocale,\n Domains extends DomainRuleMap = ResolvedDomains,\n> = L extends string\n ? [string] extends [L] // L is wide (string / LocalesValues) → distribute over declared locales\n ? GetPrefixResultNarrowed<DeclaredLocales, Mode, Default, Domains>\n : IsDomainExclusive<L, Domains> extends true\n ? { prefix: ''; localePrefix: undefined } // the domain identifies the locale\n : [string] extends [Mode]\n ? GetPrefixResult // mode is wide → fall back to generic result\n : Mode extends 'prefix-all'\n ? { prefix: `${L}/`; localePrefix: L }\n : Mode extends 'prefix-no-default'\n ? L extends Default\n ? { prefix: ''; localePrefix: undefined }\n : { prefix: `${L}/`; localePrefix: L }\n : { prefix: ''; localePrefix: undefined } // no-prefix / search-params\n : { prefix: ''; localePrefix: undefined }; // locale is undefined\n\n/**\n * Determines the URL prefix for a given locale based on the routing mode configuration.\n *\n * Example:\n *\n * ```ts\n * // prefix-no-default mode with default locale\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // prefix-no-default mode with non-default locale\n * getPrefix('fr', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '/fr', localePrefix: 'fr' }\n *\n * // prefix-all mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-all' })\n * // Returns { prefix: '/en', localePrefix: locale }\n *\n * // search-params mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'search-params' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // no-prefix mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'no-prefix' })\n * // Returns { prefix: '', localePrefix: undefined }\n * ```\n *\n * @param locale - The locale to check for prefix. If not provided, uses configured default locale.\n * @param options - Configuration options\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @returns An object containing pathPrefix, prefix, and localePrefix for the given locale.\n */\nexport const getPrefix = <const L extends LocalesValues | undefined>(\n locale: L,\n options: RoutingOptions = {}\n): GetPrefixResultNarrowed<L> => {\n const { defaultLocale, mode, locales, domains } =\n resolveRoutingConfig(options);\n\n if (\n (process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'prefix-all' &&\n process.env.INTLAYER_ROUTING_MODE !== 'prefix-no-default') ||\n !locale ||\n !isDeclaredLocale(locale, locales)\n ) {\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n }\n\n // If this locale is the only one assigned to its domain, no URL prefix is needed\n // (the domain itself identifies the locale). Shared domains use normal prefix logic.\n if (\n process.env.INTLAYER_ROUTING_DOMAINS !== 'false' &&\n isLocaleExclusiveOnDomain(locale as LocalesValues, domains)\n ) {\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n }\n\n // Handle prefix-based modes (prefix-all or prefix-no-default)\n const shouldPrefix =\n mode === 'prefix-all' ||\n (mode === 'prefix-no-default' && defaultLocale !== locale);\n\n if (shouldPrefix) {\n return {\n prefix: `${locale}/`,\n localePrefix: locale as DeclaredLocales,\n } as GetPrefixResultNarrowed<L>;\n }\n\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n};\n"],"mappings":";;;;;;;;;;AA6CA,MAAa,wBACX,UAA0B,CAAC,OAKvB;CACJ,GAAG;CACH,eAAgB,QAAQ,iBACtBA,6CAAsB,iBACtBC;CACF,MAAM,QAAQ,QAAQC,gCAAS,QAAQC;CACvC,SAAU,QAAQ,WAChBH,6CAAsB,WACtBI;CACF,SAAS,QAAQ,WAAWF,gCAAS;CACrC,SAAS,QAAQ,WAAWA,gCAAS;AACvC;;;;;;;;;;;;;AAcA,MAAa,oBACX,OACA,YAEA,CAAC,CAAC,UAAU,WAAWF,4CAAqB,QAAO,CAAE,SAAS,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFrE,MAAa,aACX,QACA,UAA0B,CAAC,MACI;CAC/B,MAAM,EAAE,eAAe,MAAM,SAAS,YACpC,qBAAqB,OAAO;CAE9B,IACG,QAAQ,IAAI,yBACX,QAAQ,IAAI,0BAA0B,gBACtC,QAAQ,IAAI,0BAA0B,uBACxC,CAAC,UACD,CAAC,iBAAiB,QAAQ,OAAO,GAEjC,OAAO;EACL,QAAQ;EACR,cAAc;CAChB;CAKF,IACE,QAAQ,IAAI,6BAA6B,WACzCK,2DAA0B,QAAyB,OAAO,GAE1D,OAAO;EACL,QAAQ;EACR,cAAc;CAChB;CAQF,IAHE,SAAS,gBACR,SAAS,uBAAuB,kBAAkB,QAGnD,OAAO;EACL,QAAQ,GAAG,OAAO;EAClB,cAAc;CAChB;CAGF,OAAO;EACL,QAAQ;EACR,cAAc;CAChB;AACF"}